package com.zhijian.warehouse.manage.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.format.FastDateFormat;
import com.zhijian.util.BeanUtil;
import com.zhijian.util.page.Page;
import com.zhijian.warehouse.manage.dao.StoreOutRecordMapper;
import com.zhijian.warehouse.manage.entity.dto.stock.StockActionDto;
import com.zhijian.warehouse.manage.entity.dto.stock.StockStorageDto;
import com.zhijian.warehouse.manage.entity.export.StoreOutRecordExport;
import com.zhijian.warehouse.manage.entity.po.Product;
import com.zhijian.warehouse.manage.entity.po.StoreOutRecord;
import com.zhijian.warehouse.manage.entity.po.Warehouse;
import com.zhijian.warehouse.manage.entity.po.WarehouseLocation;
import com.zhijian.warehouse.manage.entity.vo.request.*;
import com.zhijian.warehouse.manage.entity.vo.response.StoreLockVo;
import com.zhijian.warehouse.manage.entity.vo.response.StoreOutProductDetail;
import com.zhijian.warehouse.manage.entity.vo.response.StoreOutRecordProductVo;
import com.zhijian.warehouse.manage.entity.vo.response.StoreOutRecordVo;
import com.zhijian.warehouse.manage.enums.CodingEnum;
import com.zhijian.warehouse.manage.enums.StockActionEnum;
import com.zhijian.warehouse.manage.enums.StoreEnum;
import com.zhijian.warehouse.manage.service.*;
import com.zhijian.warehouse.manage.util.AssertCommonUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 出库记录业务处理层
 *
 * @author auto
 * @version: 1.0
 * @date 2024-10-27 13:45:40
 */
@Slf4j
@Service
public class StoreOutRecordServiceImpl implements StoreOutRecordService {

    @Autowired
    private StoreOutRecordMapper mapper;

    @Autowired
    private StoreOutRecordProductService storeOutRecordProductService;

    @Autowired
    private StoreService storeService;

    @Autowired
    private StoreLockService storeLockService;

    @Autowired
    private ProductService productService;
    @Autowired
    private WarehouseService warehouseService;
    @Autowired
    private WarehouseLocationService warehouseLocationService;


    @Override
    public StoreOutRecord selectById(Long id) {
        return mapper.selectByPrimaryKey(id);
    }

    @Override
    public StoreOutRecordVo selectVoById(Long id) {
        StoreOutRecordVo storeOutRecordVo = mapper.selectVoById(id);
        if (Objects.isNull(storeOutRecordVo)) {
            return null;
        }
        //产品
        List<StoreOutRecordProductVo> storeOutRecordProductVos = storeOutRecordProductService.selectList(StoreOutRecordProductSearchVo.builder()
                .storeOutRecordId(id)
                .build());
        if (BeanUtil.isNotEmpty(storeOutRecordProductVos))
            storeOutRecordProductVos.forEach(vo -> {
                //根据产品id查询仓库id
                List<Long> warehouseIdsList = storeService.selectWarehouseId(vo.getProductId());
                //组装当前库存和仓库库位信息
                vo.setStoreWarehouseVos(storeService.getWarehouseWithLocations(warehouseIdsList, vo.getProductId()));
            });
        storeOutRecordVo.setStoreOutRecordProductList(storeOutRecordProductVos);
        return storeOutRecordVo;
    }

    @Override
    public void deleteById(Long id) {
        StoreOutRecord storeOutRecord = this.selectById(id);
        AssertCommonUtil.check(Objects.isNull(storeOutRecord)
                || storeOutRecord.getStatus() == StoreEnum.StatusEnum.END.getCode().byteValue(), "审核通过的单据无法删除");
        storeOutRecordProductService.deleteByCondition(StoreOutRecordProductSearchVo.builder()
                .storeOutRecordId(id)
                .build());
        mapper.deleteByPrimaryKey(id);
    }

    @Override
    public void deleteByIdList(List<Long> idList) {
        if (CollectionUtil.isNotEmpty(idList)) {
            for (Long id : idList) {
                deleteById(id);
            }
        }
    }

    @Override
    public Long insert(StoreOutRecord storeOutRecord) {
        Date now = new Date();
        storeOutRecord.setCreateTime(now);
        storeOutRecord.setUpdateTime(now);
        return mapper.insert(storeOutRecord) > 0 ? storeOutRecord.getId() : 0L;
    }

    @Override
    public Long insertSelective(StoreOutRecord storeOutRecord) {
        Date now = new Date();
        storeOutRecord.setCreateTime(now);
        storeOutRecord.setUpdateTime(now);
        return mapper.insertSelective(storeOutRecord) > 0 ? storeOutRecord.getId() : 0L;
    }

    @Override
    public boolean update(StoreOutRecord storeOutRecord) {
        Date now = new Date();
        storeOutRecord.setUpdateTime(now);
        return mapper.updateByPrimaryKey(storeOutRecord) != 0;
    }

    @Override
    public boolean updateByPrimaryKeySelective(StoreOutRecord record) {
        Date now = new Date();
        record.setUpdateTime(now);
        return mapper.updateByPrimaryKeySelective(record) != 0;
    }

    @Override
    @Transactional
    public boolean save(StoreOutRecordSaveOrUpdateVo saveOrUpdateVo) {
        saveOrUpdateVo.setOutTime(BeanUtil.isEmpty(saveOrUpdateVo.getOutTime()) ?
                DateUtil.format(new Date(), com.zhijian.util.DateUtil.DATE_FORMAT) :
                saveOrUpdateVo.getOutTime());
        if (BeanUtil.isNotEmpty(saveOrUpdateVo.getId()))
            return this.modify(saveOrUpdateVo);
        StoreOutRecord storeOutRecord = new StoreOutRecord();
        BeanUtils.copyProperties(saveOrUpdateVo, storeOutRecord);
        storeOutRecord.setNumber(createNextNumber());
        storeOutRecord.setStatus(storeOutRecord.getStatus() == null ? StoreEnum.StatusEnum.DRAFT.getCode().byteValue() : storeOutRecord.getStatus());
        //先保存入库单
        Long storeRecordId = this.insertSelective(storeOutRecord);
        saveOrUpdateVo.setId(storeRecordId);
        //产品信息
        List<StoreOutRecordProductSaveOrUpdateVo> storeRecordProductList = saveOrUpdateVo.getStoreOutRecordProductList();
        checkInventory(storeRecordProductList);
        if (BeanUtil.isNotEmpty(storeRecordProductList))
            for (StoreOutRecordProductSaveOrUpdateVo storeOutRecordProductSaveOrUpdateVo : storeRecordProductList) {
                storeOutRecordProductSaveOrUpdateVo.setStoreOutRecordId(storeRecordId);
                storeOutRecordProductService.save(storeOutRecordProductSaveOrUpdateVo);
            }
        return storeRecordId != 0;
    }

    /**
     * 校验数据出库数量是否大于库存数量
     *
     * @param storeRecordProductList
     */
    private void checkInventory(List<StoreOutRecordProductSaveOrUpdateVo> storeRecordProductList) {
        if (BeanUtil.isEmpty(storeRecordProductList))
            return;
        storeRecordProductList.forEach(storeRecordProduct -> {
            if (BeanUtil.isNotEmpty(storeRecordProduct.getStoreCount())
                    && BeanUtil.isNotEmpty(storeRecordProduct.getWarehouseId())
                    && BeanUtil.isNotEmpty(storeRecordProduct.getWarehouseLocationId())) {

                // 查询现库存
                String build = storeService.selectProduct(StoreSearchVo.builder()
                        .warehouseId(storeRecordProduct.getWarehouseId())
                        .warehouseLocationId(storeRecordProduct.getWarehouseLocationId())
                        .productId(storeRecordProduct.getProductId())
                        .build());

                if (storeRecordProduct.getStoreCount().compareTo(new BigDecimal(build)) > 0) {
                    Product product = productService.selectById(storeRecordProduct.getProductId());
                    Warehouse warehouse = warehouseService.selectById(storeRecordProduct.getWarehouseId());
                    WarehouseLocation warehouseLocation = warehouseLocationService.selectById(storeRecordProduct.getWarehouseLocationId());
                    AssertCommonUtil.check(true,
                            String.format("库存不足，仓库:%s库位:%s的产品:%s库存为%s，需要出库%s",
                                    warehouse.getName(),
                                    warehouseLocation.getName(),
                                    product.getName() == null ? "" : product.getName(),
                                    build,
                                    storeRecordProduct.getStoreCount()));
                }
            }
        });
    }


    @Override
    @Transactional
    public boolean modify(StoreOutRecordSaveOrUpdateVo saveOrUpdateVo) {
        if (BeanUtil.isEmpty(saveOrUpdateVo.getId()))
            return this.save(saveOrUpdateVo);
        StoreOutRecord old = this.selectById(saveOrUpdateVo.getId());
        AssertCommonUtil.notNull(old, "此数据不存在或者已被删除");

        AssertCommonUtil.check(old.getStatus() == StoreEnum.StatusEnum.END.getCode().byteValue(), "当前出库单已审核通过，不能修改");
        old.setPlanCount(saveOrUpdateVo.getPlanCount());
        old.setRealCount(saveOrUpdateVo.getRealCount());
        old.setWarehouseId(saveOrUpdateVo.getWarehouseId());
        old.setNote(saveOrUpdateVo.getNote());
        old.setUpdateUserId(saveOrUpdateVo.getUpdateUserId());
        old.setUpdateUserName(saveOrUpdateVo.getUpdateUserName());
        this.update(old);
        storeOutRecordProductService.deleteByCondition(StoreOutRecordProductSearchVo.builder()
                .storeOutRecordId(saveOrUpdateVo.getId())
                .build());
        //产品信息
        List<StoreOutRecordProductSaveOrUpdateVo> storeRecordProductList = saveOrUpdateVo.getStoreOutRecordProductList();
        checkInventory(storeRecordProductList);
        if (BeanUtil.isNotEmpty(storeRecordProductList))
            for (StoreOutRecordProductSaveOrUpdateVo storeRecordProductSaveOrUpdateVo : storeRecordProductList) {
                //循环保存
                storeRecordProductSaveOrUpdateVo.setStoreOutRecordId(saveOrUpdateVo.getId());
                storeOutRecordProductService.save(storeRecordProductSaveOrUpdateVo);
            }
        return true;
    }

    @Override
    public StoreOutRecordVo selectOne(StoreOutRecordSearchVo storeOutRecordSearchVo) {
        return mapper.selectOne(storeOutRecordSearchVo);
    }

    @Override
    public Integer count(StoreOutRecordSearchVo storeOutRecordSearchVo) {
        setAppStatusList(storeOutRecordSearchVo);
        return mapper.count(storeOutRecordSearchVo);
    }

    @Override
    public List<StoreOutRecordVo> selectList(StoreOutRecordSearchVo storeOutRecordSearchVo) {
        setAppStatusList(storeOutRecordSearchVo);
        return mapper.selectList(storeOutRecordSearchVo);
    }

    @Override
    public List<StoreOutRecordExport> selectStoreOutExportList(StoreOutRecordSearchVo param) {
        setAppStatusList(param);
        return mapper.selectStoreOutExportList(param);
    }

    private void setAppStatusList(StoreOutRecordSearchVo storeRecordSearchVo) {
        List<Byte> list = new ArrayList<>();
        if (storeRecordSearchVo.getAppStatus() != null) {
            if (storeRecordSearchVo.getAppStatus() == 0) {
                list = Arrays.asList(
                        StoreEnum.StatusEnum.END.getCode().byteValue(),
                        StoreEnum.StatusEnum.WAIT_APPROVE.getCode().byteValue()
                );
            } else if (storeRecordSearchVo.getAppStatus() == 1) {
                list = Collections.singletonList(StoreEnum.StatusEnum.WAIT_APPROVE.getCode().byteValue());
            } else if (storeRecordSearchVo.getAppStatus() == 2) {
                list = Collections.singletonList(StoreEnum.StatusEnum.END.getCode().byteValue());
            }
        }
        storeRecordSearchVo.setAppStatusList(list);
    }

    @Override
    public Page<StoreOutRecordVo> selectPage(StoreOutRecordSearchVo storeOutRecordSearchVo) {
        AssertCommonUtil.check(Objects.isNull(storeOutRecordSearchVo.getPageNum()) || Objects.isNull(storeOutRecordSearchVo.getPageSize()), "分页参数不能为空");
        //先直接查总数
        int count = count(storeOutRecordSearchVo);
        //分页
        Page<StoreOutRecordVo> page = new Page<>(true, count, storeOutRecordSearchVo.getPageNum(), storeOutRecordSearchVo.getPageSize());
        storeOutRecordSearchVo.setStartIndex(page.getStartIndex());
        page.setPage(true);
        page.setTotalCount(count);
        List<StoreOutRecordVo> responseVos = this.selectList(storeOutRecordSearchVo);
        responseVos.forEach(storeRecordVo -> {
            // 拼接字段
            StringBuilder concatenatedFields = new StringBuilder();
            if (BeanUtil.isNotEmpty(storeRecordVo.getProductDetailList())) {
                storeRecordVo.getProductDetailList().forEach(storeInProductDetail -> {
                    concatenatedFields.append(storeInProductDetail.getProductNumber()).append("-");
                    concatenatedFields.append(storeInProductDetail.getProductName()).append("-");
                    concatenatedFields.append(storeInProductDetail.getProductModel()).append("-");
                    concatenatedFields.append(storeInProductDetail.getStoreCount()).append(";").append(System.lineSeparator());
                });
                storeRecordVo.setProductDetail(concatenatedFields.toString());
                //获取仓库名称并去重后以都逗号分隔
                List<String> warehouseNames = storeRecordVo.getProductDetailList().stream().map(StoreOutProductDetail::getWarehouseName).distinct().collect(Collectors.toList());
                storeRecordVo.setWarehouseName(String.join(",", warehouseNames));
                storeRecordVo.setProductDetailList(null);
            }
        });


        page.setData(responseVos);
        return page;
    }

    @Override
    public boolean insertBatch(List<StoreOutRecord> entities) {
        return CollectionUtil.isNotEmpty(entities) && mapper.insertBatch(entities) > 0;
    }

    @Override
    public void deleteByCondition(StoreOutRecordSearchVo storeOutRecordSearchVo) {
        if (BeanUtil.isEmpty(storeOutRecordSearchVo.getId()))
            return;
        List<StoreOutRecordVo> storeOutRecordVos = this.selectList(storeOutRecordSearchVo);
        if (BeanUtil.isEmpty(storeOutRecordVos))
            return;
        //获取id
        List<Long> ids = storeOutRecordVos.stream().map(StoreOutRecordVo::getId).collect(Collectors.toList());
        //删除产品信息
        this.deleteByIdList(ids);
    }

    @Override
    public String selectMaxNumber() {
        return mapper.selectMaxNumberByDay(DateUtil.formatDate(new Date()));
    }

    @Override
    public String createNextNumber() {
        String maxCode = this.selectMaxNumber();
        if (StringUtils.isBlank(maxCode)) {
            return String.format("%s-%s-001", CodingEnum.PREFIX.CK.value, DateUtil.format(new Date(), FastDateFormat.getInstance("yyyyMMdd")));
        }
        String[] maxCodeArray = maxCode.split("-");
        Integer i = Integer.valueOf(maxCodeArray[maxCodeArray.length - 1]);
        i++;
        return String.format("%s-%s-%s", CodingEnum.PREFIX.CK.value, DateUtil.format(new Date(), FastDateFormat.getInstance("yyyyMMdd")), String.format("%03d", i));
    }


    @Transactional
    @Override
    public boolean submitAudit(StoreOutRecordSaveOrUpdateVo param) {
        List<StoreOutRecordProductVo> storeOutRecordProductList;
        modify(param);
        StoreOutRecordVo storeOutRecordVo = this.selectVoById(param.getId());
        storeOutRecordProductList = storeOutRecordVo.getStoreOutRecordProductList();
        StoreOutRecord storeOutRecord = selectById(param.getId());

        AssertCommonUtil.check(Objects.equals(StoreEnum.StatusEnum.END.getCode().byteValue(), storeOutRecord.getStatus()), "出库单已完成审核，无法提交审核");

        AssertCommonUtil.check(BeanUtil.isEmpty(storeOutRecordProductList), "商品明细不得为空");
        storeOutRecordProductList.forEach(vo -> {
            AssertCommonUtil.check(BeanUtil.isEmpty(vo.getWarehouseLocationId()), "产品明细中库位信息不得为空");
            AssertCommonUtil.check(BeanUtil.isEmpty(vo.getStoreCount()), "产品明细中数量不得为空");
            AssertCommonUtil.check(BeanUtil.isEmpty(vo.getWarehouseId()), "产品明细中仓库不得为空");
        });
        Set<Long> warehouseIds = storeOutRecordProductList.stream().map(StoreOutRecordProductVo::getWarehouseId).collect(Collectors.toSet());
        warehouseIds.forEach(p -> {
            StoreLockVo storeLock = storeLockService.selectByWarehouseId(p);
            AssertCommonUtil.check(Objects.equals(storeLock.getType(), StoreEnum.LOCK_TYPE.LOCK.getCode()), String.format("仓库%s已被锁定，无法提交审核", storeLock.getWarehouseName()));
        });
        checkInventory(param.getStoreOutRecordProductList());
        //更新审核状态
        updateByPrimaryKeySelective(StoreOutRecord.builder().id(param.getId())
                .status(StoreEnum.StatusEnum.WAIT_APPROVE.getCode().byteValue())
                .updateUserId(param.getUpdateUserId())
                .updateTime(new Date())
                .build());
        return true;
    }


    @Override
    public boolean audit(StoreOutRecordSearchVo param, Integer sourceType) {
        StoreOutRecord storeOutRecord = selectById(param.getId());
        AssertCommonUtil.check(BeanUtil.isEmpty(storeOutRecord), "出库单不存在");
        String outTime = param.getOutTime();
        if (BeanUtil.isEmpty(outTime) && param.getStatus() == StoreEnum.StatusEnum.END.getCode().byteValue())
            outTime = DateUtil.format(new Date(), com.zhijian.util.DateUtil.DATE_FORMAT);
        //修改
        updateByPrimaryKeySelective(StoreOutRecord.builder().id(param.getId())
                .status(param.getStatus())
                .rejectMsg(param.getRejectMsg())
                .updateUserId(param.getUpdateUserId())
                .updateTime(new Date())
                .outTime(BeanUtil.isEmpty(outTime) ? outTime : param.getOutTime())
                .build());
        //如果审核通过，则变更库存
        //审核通过
        if (param.getStatus() == StoreEnum.StatusEnum.END.getCode().byteValue()) {
            StoreOutRecordVo storeRecordVo = selectVoById(param.getId());
            AssertCommonUtil.check(BeanUtil.isEmpty(storeRecordVo) || BeanUtil.isEmpty(storeRecordVo.getStoreOutRecordProductList()), "入库单不存在或无产品信息");
            List<StockStorageDto> stockStorages = new ArrayList<>();
            List<StoreOutRecordProductVo> dets = storeRecordVo.getStoreOutRecordProductList();
            dets.forEach(p -> {
                boolean flag = storeLockService.selectLock(p.getWarehouseId());
                if (flag) {
                    Warehouse warehouse = warehouseService.selectById(storeOutRecord.getWarehouseId());
                    AssertCommonUtil.check(flag, String.format("仓库%已锁盘，无法审核出库单", warehouse == null ? " " : warehouse.getName()));
                }
                stockStorages.add(StockStorageDto.builder().productId(p.getProductId())
                        .warehouseLocationId(p.getWarehouseLocationId())
                        .warehouseId(p.getWarehouseId())
                        .count(p.getStoreCount())
                        .build());
            });
            StockActionDto action = StockActionDto.builder()
                    .actionType(StockActionEnum.ACTION_TYPE.STOCK_OUT)
                    .stockStorages(stockStorages).voucherCode(storeRecordVo.getNumber())
                    .optId(storeRecordVo.getId())
                    .optSource(sourceType)
                    .voucherTime(com.zhijian.util.DateUtil.getTime(com.zhijian.util.DateUtil.DATE_FORMAT))
                    .optUserId(param.getUpdateUserId())
                    .crtUserId(storeRecordVo.getCreateUserId())
                    .build();
            storeService.stockAction(action);
        }
        return true;
    }
}
